//
//  Executor.cpp
//  Executor
//
//  Created by Chris Wong on 4/30/12.
//  Copyright 2012 __MyCompanyName__. All rights reserved.
//

#include "Executor.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <pthread.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

Executor::Executor(){}

void *run(void *args)
{
    struct threadInfo *argt;
    argt = (struct threadInfo *)args;
    std::string prevOutput;
    if(argt->fileIN != NULL){
        //We have a file to read.
        std::ifstream t(argt->fileIN);
        std::stringstream buffer;
        buffer << t.rdbuf();
        prevOutput = buffer.str();
    }


    while(argt->args[argt->commandCounter] != NULL){
        int ppc[2];
        int pcp[2];
        int pcpe[2];
        if(pipe(pcp) ==-1 || pipe(ppc) ==-1 || pipe(pcpe) == -1){
            perror("pipe");
            pthread_exit(NULL);
        }
        int pid = fork();

        if(pid > 0){
            char buf = NULL;
            char bufe = NULL;
            argt->procID = pid;


            int exitStatus;

            close(pcp[1]);      //close write end of Child->Parent pipe
            close(ppc[0]);
            close(pcpe[1]);

            char tmp;
            int x = 0;
            //std::cout << prevOutput << std::endl;
            while ((tmp = prevOutput[x]) != NULL) {
                write(ppc[1], &tmp, 1);
                x++;
            }
            close(ppc[1]);
            prevOutput.empty();

            //TO STDOUT
            while((read(pcp[0], &buf, 1)>0) || (read(pcpe[0], &bufe, 1)>0))
            {

                prevOutput.append(1,buf);
                if(argt->args[argt->commandCounter + 1] == NULL){
                    argt->error.append(1,bufe);
                    argt->output.append(1,buf);
                    if(!argt->background && argt->fileOUT==NULL && argt->fileERROR==NULL){
                        std::cout << buf;
                        std::cout << bufe;
                    }
                }
                buf = NULL;
                bufe = NULL;
            }



            wait(&exitStatus);
            //Close the other side of pipe
            //When done clos everything
            argt->exitStatus = exitStatus;

            if(argt->args[argt->commandCounter + 1] == NULL){
                close(ppc[1]);
                close(pcp[0]);
                close(pcpe[0]);
                argt->isDone = true;
                //write output to fileout
                if(argt->fileOUT != NULL){
                    std::ofstream outFile;
                    outFile.open (argt->fileOUT);
                    outFile << argt->output;
                    outFile.close();
                }
                if(argt->fileERROR != NULL){
                    std::ofstream errorFile;
                    errorFile.open (argt->fileERROR);
                    errorFile << argt->error;
                    errorFile.close();
                }
                return 0;
            }



        }
        else if(pid == 0)
        {
            //I am the child
            close(ppc[1]);
            //Duplicate the read pipe to the stdin
            if(argt->commandCounter != 0 || !prevOutput.empty()){
            //while (read(ppc[0], &buf, 1) > 0)
            //    write(STDIN_FILENO, &buf, 1);
                dup2(ppc[0],0);
            }

            // Pipe the stderr to parent
            if(dup2(pcpe[1], 2) == -1){
                perror("Child cannot set stderr pipe to parent");
            }

            // Pipe the stdout to parent
            if(dup2(pcp[1], 1) == -1){
                perror("Child cannot set sdtout pipe to parent");
            }

            //Confusing: 0: read
            //           1: write

            close(pcp[0]); //Close the read end of the child->pipe end
            close(pcp[1]);
            close(pcpe[1]);
            close(pcpe[0]);
            if(argt->args[argt->commandCounter + 1] == NULL){
                close(ppc[1]); //Close the write end of the parent->child pipe;
                close(ppc[0]);

            }


            //We can also store the return in a string
           /// std::cout << argt->args[argt->commandCounter][0] << "  - - - " << argt->args[argt->commandCounter][1] << std::endl;

            ///std::cout << sizeof(args) << " + + " << sizeof(argt->args[argt->commandCounter]) << std::endl;

            execv(argt->args[argt->commandCounter][0], argt->args[argt->commandCounter]);
            std::cout << "Error" << std::endl;
            //exit(-1); // should not run here

        }
        else{
            //error creating childs
            std::cout <<  "Error creating child process" <<std::endl;
        }


        argt->commandCounter ++;

    }

    pthread_exit(NULL);

}

void threadRunner(threadInfo *temp){
    //struct arguments threadArgs = temp;
    pthread_t newThread;
    pthread_create(&newThread, NULL, run, (void*)temp);
    if((*temp).background == true){
        pthread_detach(NULL);
    }else{
        pthread_join(newThread, NULL);
    }

}
void Executor::runThread(char **args[], bool background, char * inenvVars[], threadInfo *thread)
{

    struct threadInfo temp;
    (*thread).background = background;
    (*thread).commandCounter = 0;

    (*thread).args = args;

    (*thread).envVars = inenvVars;
    (*thread).isDone = false;
    (*thread).output.clear();

    threadRunner(thread);
}
